﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using LitJson;

namespace Framework
{
    public static class StringExtension
    {
        #region 检测正则
        private static readonly char[] IllegalUrlCharacters = new[] { ';', '/', '\\', '?', ':', '@', '&', '=', '+', '$', ',', '<', '>', '#', '%', '.', '!', '*', '\'', '"', '(', ')', '[', ']', '{', '}', '|', '^', '`', '~', '–', '‘', '’', '“', '”', '»', '«' };
        private static readonly Regex WebUrlExpression = new Regex(@"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled);
        private static readonly Regex EmailExpression = new Regex(@"^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$", RegexOptions.Singleline | RegexOptions.Compiled);
        private static readonly Regex MobilePhoneNumberExpression = new Regex(@"^((\+86)|(86))?1[0-9][0-9]{9}$");
        private static readonly Regex StripHtmlExpression = new Regex("<\\S[^><]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        private static readonly Regex TelephoneNumberExpression = new Regex(@"^(0[0-9]{2,3}\-)?([2-9][0-9]{6,7})+(\-[0-9]{1,4})?$");
        private static readonly Regex TelephoneOrMobileNumberExpression = new Regex(@"((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)");
        private static readonly Regex IdentityCardExpression = new Regex(@"^(^\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$");
        private static readonly Regex QqExpression = new Regex(@"^\d{4,20}$");
        #endregion
        #region 安全检测
        /// <summary>
        /// 是否为Null或者空
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string target)
        {
            return String.IsNullOrEmpty(target);
        }
        /// <summary>
        /// 判断是否为Null或空白
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNullOrWhiteSpace(string value)
        {
            if (String.IsNullOrEmpty(value))
            {
                return true;
            }
            return String.IsNullOrEmpty(value.Trim());
        }
        #endregion
        #region 字符串类型检测
        [DebuggerStepThrough]
        public static bool IsWebUrl(this string target)
        {
            return !String.IsNullOrEmpty(target) && WebUrlExpression.IsMatch(target);
        }
        [DebuggerStepThrough]
        public static bool IsEmail(this string target)
        {
            return !String.IsNullOrEmpty(target) && EmailExpression.IsMatch(target);
        }
        [DebuggerStepThrough]
        public static bool IsTelephoneNumber(this string target)
        {
            return !String.IsNullOrEmpty(target) && TelephoneNumberExpression.IsMatch(target);
        }
        [DebuggerStepThrough]
        public static bool IsTelephoneOrMobileNumber(this string target)
        {
            return !String.IsNullOrEmpty(target) && TelephoneOrMobileNumberExpression.IsMatch(target);
        }
        [DebuggerStepThrough]
        public static bool IsMobilePhoneNumber(this string target)
        {
            return !String.IsNullOrEmpty(target) && MobilePhoneNumberExpression.IsMatch(target);
        }
        [DebuggerStepThrough]
        public static bool IsIdentityCard(this string target)
        {
            return !String.IsNullOrEmpty(target) && IdentityCardExpression.IsMatch(target);
        }
        [DebuggerStepThrough]
        public static bool IsQq(this string target)
        {
            return !String.IsNullOrEmpty(target) && QqExpression.IsMatch(target);
        }
        [DebuggerStepThrough]
        public static bool IsSwf(this string url)
        {
            url = url ?? String.Empty;
            var webSwfExpression = new Regex(@"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)\.swf?",
                                             RegexOptions.Singleline | RegexOptions.Compiled);
            if (webSwfExpression.IsMatch(url))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 是否为ip
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        public static bool IsIp(this string ip)
        {
            return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
        }

        public static bool IsIpSect(this string ip)
        {
            return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){2}((2[0-4]\d|25[0-5]|[01]?\d\d?|\*)\.)(2[0-4]\d|25[0-5]|[01]?\d\d?|\*)$");
        }

        /// <summary>
        /// 判断字符串是否是yy-mm-dd字符串
        /// </summary>
        /// <param name="target">待判断字符串</param>
        /// <returns>判断结果</returns>
        public static bool IsDateString(this string target)
        {
            return Regex.IsMatch(target, @"(\d{4})-(\d{1,2})-(\d{1,2})");
        }

        /// <summary>
        /// 检查颜色值是否为3/6位的合法颜色
        /// </summary>
        /// <param name="color">待检查的颜色</param>
        /// <returns></returns>
        public static bool IsColorValue(this string color)
        {
            if (color.IsNullOrEmpty())
                return false;
            color = color.Trim().Trim('#');
            if (color.Length != 3 && color.Length != 6)
                return false;
            //不包含0-9  a-f以外的字符
            if (!Regex.IsMatch(color, "[^0-9a-f]", RegexOptions.IgnoreCase))
                return true;
            return false;
        }

        #endregion
        #region 字符串转化安全
        /// <summary>
        /// NULL字符串转化为安全
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static string NullSafe(this string target)
        {
            return (target ?? String.Empty).Trim();
        }
        /// <summary>
        /// NULL空白字符串转化为安全
        /// </summary>
        /// <param name="target"></param>
        /// <param name="defalue"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static string EmptySafe(this string target, string defalue)
        {
            Check.Argument.IsNotEmpty(target, "target");
            return (String.IsNullOrEmpty((target ?? String.Empty).Trim())) ? defalue : target;
        }
        #endregion
        #region 字符串转化


        /// <summary>
        /// Json特符字符过滤，参见http://www.json.org/
        /// </summary>
        /// <param name="target">要过滤的源字符串</param>
        /// <returns>返回过滤的字符串</returns>
        public static string JsonCharFilter(this string target)
        {
            target = target.Replace("\\", "\\\\");
            target = target.Replace("\b", "\\\b");
            target = target.Replace("\t", "\\\t");
            target = target.Replace("\n", "\\\n");
            target = target.Replace("\n", "\\\n");
            target = target.Replace("\f", "\\\f");
            target = target.Replace("\r", "\\\r");
            return target.Replace("\"", "\\\"");
        }
        public static String ToJson(String s)
        {
            var sb = new StringBuilder(s.Length + 20);
            sb.Append('\"');
            foreach (char c in s)
            {
                switch (c)
                {
                    case '\"':
                        sb.Append("\\\"");
                        break;
                    case '\\':
                        sb.Append("\\\\");
                        break;
                    case '/':
                        sb.Append("\\/");
                        break;
                    case '\b':
                        sb.Append("\\b");
                        break;
                    case '\f':
                        sb.Append("\\f");
                        break;
                    case '\n':
                        sb.Append("\\n");
                        break;
                    case '\r':
                        sb.Append("\\r");
                        break;
                    case '\t':
                        sb.Append("\\t");
                        break;
                    default:
                        sb.Append(c);
                        break;
                }
            }
            sb.Append('\"');
            return sb.ToString();
        }
        #endregion
        [DebuggerStepThrough]
        public static string FormatWith(this string target, params object[] args)
        {
            Check.Argument.IsNotEmpty(target, "target");

            return String.Format(Constants.CurrentCulture, target, args);
        }

        [DebuggerStepThrough]
        public static string Hash(this string target)
        {
            Check.Argument.IsNotEmpty(target, "target");
            using (MD5 md5 = MD5.Create())
            {
                byte[] data = Encoding.Unicode.GetBytes(target);
                byte[] hash = md5.ComputeHash(data);
                return Convert.ToBase64String(hash);
            }
        }

        [DebuggerStepThrough]
        public static string WrapAt(this string target, int index)
        {
            const int dotCount = 3;
            Check.Argument.IsNotEmpty(target, "target");
            Check.Argument.IsNotNegativeOrZero(index, "index");
            return (target.Length <= index) ? target : String.Concat(target.Substring(0, index - dotCount), new string('.', dotCount));
        }

        [DebuggerStepThrough]
        public static string StripHtml(this string target)
        {
            return StripHtmlExpression.Replace(target, String.Empty);
        }

        [DebuggerStepThrough]
        public static Guid ToGuid(this string target)
        {
            Guid result = Guid.Empty;

            if ((!String.IsNullOrEmpty(target)) && (target.Trim().Length == 22))
            {
                string encoded = String.Concat(target.Trim().Replace("-", "+").Replace("_", "/"), "==");

                try
                {
                    byte[] base64 = Convert.FromBase64String(encoded);

                    result = new Guid(base64);
                }
                catch (FormatException)
                {
                }
            }

            return result;
        }

        [DebuggerStepThrough]
        public static T ToEnum<T>(this string target, T defaultValue) where T : IComparable, IFormattable
        {
            T convertedValue = defaultValue;

            if (!String.IsNullOrEmpty(target))
            {
                try
                {
                    convertedValue = (T)Enum.Parse(typeof(T), target.Trim(), true);
                }
                catch (ArgumentException)
                {
                }
            }

            return convertedValue;
        }

        [DebuggerStepThrough]
        public static string ToLegalUrl(this string target)
        {
            if (String.IsNullOrEmpty(target))
            {
                return target;
            }

            target = target.Trim();

            if (target.IndexOfAny(IllegalUrlCharacters) > -1)
            {
                target = IllegalUrlCharacters.Aggregate(target, (current, character) => current.Replace(character.ToString(Constants.CurrentCulture), String.Empty));
            }

            target = target.Replace(" ", "-");

            while (target.Contains("--"))
            {
                target = target.Replace("--", "-");
            }

            return target;
        }

        [DebuggerStepThrough]
        public static string UrlEncode(this string target)
        {
            return HttpUtility.UrlEncode(target);
        }

        [DebuggerStepThrough]
        public static string UrlDecode(this string target)
        {
            return HttpUtility.UrlDecode(target);
        }

        [DebuggerStepThrough]
        public static string AttributeEncode(this string target)
        {
            return HttpUtility.HtmlAttributeEncode(target);
        }

        [DebuggerStepThrough]
        public static string HtmlEncode(this string target)
        {
            return HttpUtility.HtmlEncode(target);
        }

        [DebuggerStepThrough]
        public static string HtmlDecode(this string target)
        {
            return HttpUtility.HtmlDecode(target);
        }

        [DebuggerStepThrough]
        public static string Replace(this string target, ICollection<string> oldValues, string newValue)
        {
            oldValues.ForEach(oldValue => target = target.Replace(oldValue, newValue));
            return target;
        }
        [DebuggerStepThrough]
        public static DateTime ToDateTime(this string target)
        {
            DateTime dt;
            if (!DateTime.TryParse(target, out dt))
            {
                dt = DateTime.Now;
            }
            return dt;
        }
        /// <summary>
        /// 字符串截取
        /// </summary>
        /// <param name="target"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string CutString(this string target, int count)
        {
            if (String.IsNullOrEmpty(target))
            {
                return target;
            }
            if (target.Length <= count) return target;
            return target.Substring(0, count);
        }

        /// <summary>
        /// 字符串截取
        /// </summary>
        /// <param name="target"></param>
        /// <param name="count"></param>
        /// <param name="app"> </param>
        /// <returns></returns>
        public static string CutString(this string target, int count, string app)
        {
            if (String.IsNullOrEmpty(target))
            {
                return target;
            }
            if (target.Length <= count) return target;
            return target.Substring(0, count) + app;
        }
        public static T Deserialize<T>(this string json)
        {
            return JsonMapper.ToObject<T>(json);
        }
        #region 扩展方法
        /// <summary>
        /// 返回字符串真实长度, 1个汉字长度为2
        /// </summary>
        /// <returns>字符长度</returns>
        public static int GetStringLength(this string str)
        {
            return Encoding.Default.GetBytes(str).Length;
        }
        /// <summary>
        /// 将字符串转换为Color
        /// </summary>
        /// <param name="color"></param>
        /// <returns></returns>
        public static Color ToColor(this string color)
        {
            int red, green, blue;
            char[] rgb;
            color = color.TrimStart('#');
            color = Regex.Replace(color.ToLower(), "[g-zG-Z]", "");
            switch (color.Length)
            {
                case 3:
                    rgb = color.ToCharArray();
                    red = Convert.ToInt32(rgb[0].ToString(CultureInfo.InvariantCulture) + rgb[0].ToString(CultureInfo.InvariantCulture), 16);
                    green = Convert.ToInt32(rgb[1].ToString(CultureInfo.InvariantCulture) + rgb[1].ToString(CultureInfo.InvariantCulture), 16);
                    blue = Convert.ToInt32(rgb[2].ToString(CultureInfo.InvariantCulture) + rgb[2].ToString(CultureInfo.InvariantCulture), 16);
                    return Color.FromArgb(red, green, blue);
                case 6:
                    rgb = color.ToCharArray();
                    red = Convert.ToInt32(rgb[0].ToString(CultureInfo.InvariantCulture) + rgb[1].ToString(CultureInfo.InvariantCulture), 16);
                    green = Convert.ToInt32(rgb[2].ToString(CultureInfo.InvariantCulture) + rgb[3].ToString(CultureInfo.InvariantCulture), 16);
                    blue = Convert.ToInt32(rgb[4].ToString(CultureInfo.InvariantCulture) + rgb[5].ToString(CultureInfo.InvariantCulture), 16);
                    return Color.FromArgb(red, green, blue);
                default:
                    return Color.FromName(color);
            }
        }

        #endregion

        #region 字符串替换过滤

        /// <summary>
        /// 过滤字符串中的html代码
        /// </summary>
        /// <param name="target">输入字符串</param>
        /// <returns>返回过滤之后的字符串</returns>
        public static string ClearHtml(this string target)
        {
            string reStr = "";
            if (target != null)
            {
                if (target != String.Empty)
                {
                    const string pattern = "<\\/*[^<>]*>";
                    reStr = Regex.Replace(target, pattern, "");
                }
            }
            return (reStr.Replace("\\r\\n", "")).Replace("\\r", "");
        }

        /// <summary>
        /// 移除Html标记
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public static string RemoveHtmlTag(string content)
        {
            return Regex.Replace(content, @"<[^>]*>", String.Empty, RegexOptions.IgnoreCase);
        }

        /// <summary>
        /// 过滤HTML中的不安全标签
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public static string RemoveUnsafeHtml(string content)
        {
            content = Regex.Replace(content, @"(\<|\s+)o([a-z]+\s?=)", "$1$2", RegexOptions.IgnoreCase);
            content = Regex.Replace(content, @"(script|frame|form|meta|behavior|style)([\s|:|>])+", "$1.$2",
                                    RegexOptions.IgnoreCase);
            return content;
        }
        /// <summary>
        /// 清除UBB标签
        /// </summary>
        /// <param name="sDetail">帖子内容</param>
        /// <returns>帖子内容</returns>
        public static string ClearUbb(this string sDetail)
        {
            return Regex.Replace(sDetail, @"\[[^\]]*?\]", String.Empty, RegexOptions.IgnoreCase);
        }
        /// <summary>
        /// 从HTML中获取文本,保留br,p,img
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        public static string GetTextFromHmtl(this string html)
        {
            var regEx = new Regex(@"</?(?!br|/?p|img)[^>]*>", RegexOptions.IgnoreCase);
            return regEx.Replace(html, "");
        }
        /// <summary>
        /// 删除字符串尾部的回车/换行/空格
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string RTrim(string str)
        {
            for (int i = str.Length; i >= 0; i--)
            {
                if (str[i].Equals(' ') || str[i].Equals('\r') || str[i].Equals('\n'))
                {
                    str = str.Remove(i, 1);
                }
                else
                {
                    break;
                }
            }
            return str;
        }
        /// <summary>
        /// 从文章内容中获取第一张图片，用作文章缩略图
        /// </summary>
        /// <param name="articleContent"></param>
        /// <returns></returns>
        public static string GetHtmlImageUrl(string articleContent)
        {
            var r = new Regex(@"<IMG[^>]+src=\s*(?:'(?<src>[^']+)'|""(?<src>[^""]+)""|(?<src>[^>\s]+))\s*[^>]*>",
                              RegexOptions.IgnoreCase);
            MatchCollection mc = r.Matches(articleContent);
            if (mc.Count != 0)
            {
                return mc[0].Groups["src"].Value.ToLower();
            }
            return null;
        }
        #endregion


        #region SQL语句处理
        /// <summary>
        /// SQL LIKE 字符处理
        /// </summary>
        /// <param name="variable">SQL语句</param>
        /// <returns>处理后语句</returns>
        public static string SqlLike(this string variable)
        {
            var stringBuilder = new StringBuilder(variable);
            stringBuilder.Replace("[", "[[]");
            stringBuilder.Replace("%", "[%]");
            stringBuilder.Replace("_", "[_]");
            return stringBuilder.ToString();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="variable"></param>
        /// <returns></returns>
        public static string SqliteLike(this string variable)
        {
            var stringBuilder = new StringBuilder(variable);
            stringBuilder.Replace("\\", "\\\\");
            stringBuilder.Replace("%", "\\%");
            stringBuilder.Replace("_", "\\_");
            return stringBuilder.ToString();
        }

        public static bool IsSqlSafe(this string variable)
        {
            return !Regex.IsMatch(variable, @"[-|;|,|\/|\(|\)|\[|\]|\}|\{|%|@|\*|!|\']");

        }
        #region 转换sql代码（也防止sql注入式攻击，可以用于业务逻辑层，但要求UI层输入数据时候进行解码）
        public static string CheckStringLength(string inputString, Int32 maxLength)
        {
            if (!string.IsNullOrEmpty(inputString))
            {
                inputString = inputString.Trim();

                if (inputString.Length > maxLength)
                    inputString = inputString.Substring(0, maxLength);
            }
            return inputString;
        }

        public static string MyEncodeInputString(string inputString)
        {
            //要替换的敏感字
            const string sqlStr = @"and|or|exec|execute|insert|select|delete|update|alter|create|drop|count|\*|chr|char|asc|mid|substring|master|truncate|declare|xp_cmdshell|restore|backup|net +user|net +localgroup +administrators";
            try
            {
                if (!string.IsNullOrEmpty(inputString))
                {
                    const string strRegex = @"\b(" + sqlStr + @")\b";
                    var regex = new Regex(strRegex, RegexOptions.IgnoreCase);
                    //string s = Regex.Match(inputString).Value; 
                    MatchCollection matches = regex.Matches(inputString);
                    for (int i = 0; i < matches.Count; i++)
                        inputString = inputString.Replace(matches[i].Value, "[" + matches[i].Value + "]");
                }
            }
            catch
            {
                return "";
            }
            return inputString;

        }
        public static string MyDecodeOutputString(string outputstring)
        {
            //要替换的敏感字
            const string sqlStr = @"and|or|exec|execute|insert|select|delete|update|alter|create|drop|count|\*|chr|char|asc|mid|substring|master|truncate|declare|xp_cmdshell|restore|backup|net +user|net +localgroup +administrators";
            try
            {
                if (!string.IsNullOrEmpty(outputstring))
                {
                    const string strRegex = @"\[\b(" + sqlStr + @")\b\]";
                    var regex = new Regex(strRegex, RegexOptions.IgnoreCase);
                    MatchCollection matches = regex.Matches(outputstring);
                    for (int i = 0; i < matches.Count; i++)
                        outputstring = outputstring.Replace(matches[i].Value, matches[i].Value.Substring(1, matches[i].Value.Length - 2));

                }
            }
            catch
            {
                return "";
            }
            return outputstring;
        }
        #endregion

        #endregion
    }
}
